#include <stdio.h>
#include <signal.h>
#include <netinet/in.h>
#include <netinet/ether.h>
#include <net/if.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <sys/stat.h>
#include <signal.h>
#include <string.h>

#define STORE_FILE "/var/lib/tick/ddbb"
#define STORE_DIR "/var/lib/tick/"

#define debug() 1

struct __ipseek_param {
	char *dev;
	struct ether_addr *mac;
	struct in_addr *ipfound;
};

sig_atomic_t _continue = 0; /* todo, it should be atomic */

/* signal handler */
/* it stops the ipseek thread */
void signal_handler_stop(int signal)     /* todo, I don't like to mix sleep() and SIGALRM signals */
{
	if (debug()) printf("[libtick]SIGALRM sent\n");
	stop_ipseek();
}


/* Wrapper to create an ipseek thread */
void *__t_ipseek_wrapper(void *parameters)
{
	int result;
	struct __ipseek_param *ipseek_param = (struct __ipseek_param *) parameters;    
	result = ip_seek(ipseek_param->mac, ipseek_param->dev, ipseek_param->ipfound);
	return (void*) result;
}

/* Wrapper to create a dhcp traffic in a thread */
void *__t_maketraf_dhcp_wrapper(void *parameters)
{
	int result;
	struct __ipseek_param *ipseek_param = (struct __ipseek_param *) parameters;	
	struct timespec requested_time;
	requested_time.tv_sec = 1;
	requested_time.tv_nsec = 0;

	/* it makes the thread asynchronous cancelable */
	pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

	while (_continue != 0){
		nanosleep(&requested_time,NULL);
		pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);
		if (_continue != 0) result = send_dhcp_request(ipseek_param->dev);
		pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL);
	}
	return (void*) result;
}

/* Wrapper to create icmp traffic in a thread */
void *__t_maketraf_icmp_wrapper(void *parameters)
{
	int result;
	struct __ipseek_param *ipseek_param = (struct __ipseek_param *) parameters;	
	struct timespec requested_time;
	requested_time.tv_sec = 1;
	requested_time.tv_nsec = 0;
	
        while (_continue != 0) {
		nanosleep(&requested_time,NULL);
		pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);
		if (_continue != 0) result = send_icmp_class_C(ipseek_param->dev);
		pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL);
	}
	return (void*) result;
}


/* Wrapper to create arp traffic in a thread */
void *__t_maketraf_arp_wrapper(void *parameters)
{
	int result;
	struct __ipseek_param *ipseek_param = (struct __ipseek_param *) parameters;	
	struct timespec requested_time;
	requested_time.tv_sec = 1;
	requested_time.tv_nsec = 0;
	
        while (_continue != 0) {
		nanosleep(&requested_time,NULL); /* i don't like this, I have to wait until
						    pcap is ready */
		pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);
		if (_continue != 0) result = send_arp_class_C(ipseek_param->dev);
		pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL);
	}
	return (void*) result;
}


/* It reads a mac and interface and try to find the ip address of the mac */
/* It combines libipseek and libmaketraffic using threads */ 
int discover_router_address(struct ether_addr *mac_to_find, char *device, struct in_addr *ip_found )
{
	/* setting the signal SIGALRM */
	struct sigaction sa;
	memset(&sa, 0, sizeof(struct sigaction));
	sa.sa_handler = &signal_handler_stop; 
	if (sigaction(SIGALRM, &sa, NULL) != 0)
		fprintf(stderr,"[libtick]Error setting sigaction\n");
	alarm(10); /* we wait x seconds from now */
	if (debug()) printf("[libtick]Alarm activated\n");

	/* Be carefull with these variables */
	/* If main thread finishs before the threads, the next variables will be
	   deallocates and the threads will contain rubbish data */
	pthread_t t_ipseek;
	pthread_t t_maketraf_dhcp;
	pthread_t t_maketraf_icmp;
	pthread_t t_maketraf_arp;

	/* It makes a local copy of the mac and the interface */
        struct ether_addr mac;
	memcpy(&mac, mac_to_find, sizeof(struct ether_addr));

	char interface[IF_NAMESIZE];
	strncpy(interface, device, strlen(device) + 1);

	if (debug()) printf("[libtick]Seeking mac : %s\n", ether_ntoa(&mac));
	if (debug()) printf("[libtick]Using interface : %s\n", interface);

	struct __ipseek_param ipseek_param;
	ipseek_param.dev = interface;
        ipseek_param.mac = &mac;
	ipseek_param.ipfound = ip_found;

	_continue = 1;

	/* Try to find the ip setting promiscue mode in the network interface */
	pthread_create(&t_ipseek, NULL, __t_ipseek_wrapper, &ipseek_param);
	if (debug()) printf("[libtick]ip seek thread launched\n");

	pthread_create(&t_maketraf_dhcp, NULL, __t_maketraf_dhcp_wrapper, &ipseek_param);
	if (debug()) printf("[libtick]dhcp traffic thread launched\n");

	pthread_create(&t_maketraf_icmp, NULL, __t_maketraf_icmp_wrapper, &ipseek_param);
	if (debug()) printf("[libtick]icmp traffic thread launched\n");	
	
	pthread_create(&t_maketraf_arp, NULL, __t_maketraf_arp_wrapper, &ipseek_param);
	if (debug()) printf("[libtick]arp traffic thread launched\n");
	
	int ip_seek_result;
	if (debug()) printf("[libtick]Waiting for ip seek thread\n");
	pthread_join(t_ipseek,(void *) &ip_seek_result); /* waiting until ip_seek find the ip */
	if (debug()) printf("[libtick]Return value of ip_seek function : %d\n", ip_seek_result);

	/* stopping alarm */
	alarm(0);
	
        /* Stopping other threads */
	_continue = 0; 
	if (debug()) printf("[libtick]Stopping arp traffic\n");
	stop_send_arp_class_C();
	if (debug()) printf("[libtick]Stopping icmp traffic\n");
	stop_send_icmp_class_C();

	int maketraf_dhcp_result;
	if (debug()) printf("[libtick]Waiting for dhcp traffic thread\n");
	pthread_join(t_maketraf_dhcp, (void *) &maketraf_dhcp_result);
	if (debug()) printf("[libtick]Return value of maketraf_dhcp function : %d\n", maketraf_dhcp_result);

	int maketraf_icmp_result;
	if (debug()) printf("[libtick]Waiting for icmp traffic thread\n");
	pthread_join(t_maketraf_icmp, (void *) &maketraf_icmp_result);
	if (debug()) printf("[libtick]Return value of maketraf_icmp function : %d\n", maketraf_icmp_result);
	
	int maketraf_arp_result;
	if (debug()) printf("[libtick]Waiting for arp traffic thread\n");
	pthread_join(t_maketraf_arp, (void *) &maketraf_arp_result);
	if (debug()) printf("[libtick]Return value of maketraf_arp function : %d\n", maketraf_arp_result);
	
	if (ip_seek_result != 0) /* it ip_seek didn't find the ip */
		return -1;

	return 0;

	/* Comments :
	   The arp, icmp traffic and dhcp traffic maybe is sent before the network interface is listening 
	*/	
}

/* Set the ip to xxx.xxx.xxx.54 and add the default route */
int set_ip(const char *interface, const struct in_addr router_ip)
{
	struct in_addr host_ip;
	char *host_ip_pointer;
	char cmd[256];

        /* We can setup the ip to 54 for example */
	memcpy(&host_ip, &router_ip, sizeof(struct in_addr));
        host_ip_pointer = (char *) &host_ip;
	host_ip_pointer = host_ip_pointer + 3;
	*host_ip_pointer = 54;
	
	/* ifconfig cmd */
	sprintf(cmd,"ifconfig %s %s", interface , inet_ntoa(host_ip));
	if (debug()) printf("[libtick]Launching ... %s\n", cmd);
	system(cmd);

	/* reset the cmd value */
	memset(cmd, 0, 256);

	/* route cmd */
	sprintf(cmd,"route add default gw %s", inet_ntoa(router_ip));
	if (debug()) printf("[libtick]Launching ... %s\n", cmd); 
	system(cmd);

	return 0;
}

/* return > 0 if it we have already connected to this network and  */
/* return < 0 if an error */
/* return == 0 if the mac is not in the ddbb */
/* it stores the ip in ip_found */
int exists_in_ddbb(const struct ether_addr *mac_to_find, struct in_addr *ip_found)
{
	FILE *fd;
	int pos=0;
	struct ether_addr mac;
	struct in_addr ip;
	char smac[128];
	char sip[128];
       
	/* opening the file */
	fd = fopen(STORE_FILE, "r");
	if (fd == NULL ) {
		perror("error opening file:");
		return -1;
	}

	do {
		pos = fscanf(fd, "%s %s\n",smac, sip); 
		inet_aton(sip, &ip);
		memcpy(&mac, ether_aton(smac), sizeof(struct ether_addr));
		if (memcmp(&mac, mac_to_find, sizeof(struct ether_addr)) == 0) {
			if (debug()) printf("[libtick]I have found the mac in the ddb. ip : %s\n", inet_ntoa(ip));
			memcpy(ip_found, &ip, sizeof(struct in_addr));
			fclose(fd);
			return 1;
		}
	}
	while(pos != EOF);
	fclose(fd);
	if (debug()) printf("[libtick]I didn't find an ip for the mac: %s\n", ether_ntoa(mac_to_find));
	return 0;
}

/* save the mac and the ip in the file */
int save_new_addr_in_ddbb(const struct ether_addr *mac, const struct in_addr *ip)
{
	FILE *fd;
	int x;

	x = mkdir(STORE_DIR, S_IRWXU);
	if (x<0 && errno!=EEXIST) {
		perror("error creating directory:");
		return -1;
	}
	
	/* opening the file */
	fd = fopen(STORE_FILE, "a");
	if (fd == NULL ) {
		perror("error opening file:");
		return -1;
	}
	
	/* storing the info */
	fprintf(fd,"%s %s\n",ether_ntoa(mac), inet_ntoa(*ip));
	fclose(fd);
	return 0;
}


